//===============================================================================
// Microsoft patterns & practices
// Mobile Client Software Factory - July 2006
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using System.Reflection;
using System.CodeDom;
using System.IO;
using Microsoft.Cci;

namespace Microsoft.Practices.ObjectBuilder.ObGen
{
	/// <summary>
	/// Generator class that processes assemblies and generates code for them based on 
	/// a set of <see cref="IPolicyGenerator"/> instances.
	/// </summary>
	public class AssemblyGenerator
	{
		List<CodeCompileUnit> compileUnits = new List<CodeCompileUnit>();
		IEnumerable<IPolicyGenerator> policyGenerators;

		/// <summary>
		/// Initializes the assembly generator with the list of 
		/// policy generators that should be used to process types in the 
		/// assemblies.
		/// </summary>
		public AssemblyGenerator(IEnumerable<IPolicyGenerator> policyGenerators)
		{
			this.policyGenerators = policyGenerators;
		}

		/// <summary>
		/// Processes an assembly and generates a <see cref="CodeCompileUnit"/> for it. This unit 
		/// is added to the <see cref="CompileUnits"/> generated so far. The returned value 
		/// can be used to generate code or compile the single unit corresponding to the 
		/// input assembly.
		/// </summary>
		public CodeCompileUnit AddAssembly(Assembly assemblyToProcess)
		{
			PolicyProviderGenerator providerGenerator = new PolicyProviderGenerator();

			foreach (Type type in assemblyToProcess.GetExportedTypes())
			{
				providerGenerator.AddProvider(type, policyGenerators);
			}

			AssemblyNode asmNode = AssemblyNode.GetAssembly(assemblyToProcess);
			AddDependencies(providerGenerator.CompileUnit, asmNode.AssemblyReferences);

			return providerGenerator.CompileUnit;
		}

		private void AddDependencies(CodeCompileUnit unit, AssemblyReferenceList assemblyReferences)
		{
			foreach (AssemblyReference dependency in assemblyReferences)
			{
				string assemblyFile = dependency.Assembly.Location;
				if (File.Exists(assemblyFile) && !ContainsReference(unit.ReferencedAssemblies, assemblyFile))
				{
					unit.ReferencedAssemblies.Add(assemblyFile);
					AddDependencies(unit, dependency.Assembly.AssemblyReferences);
				}
			}
		}

		/// <devdoc>
		/// Does a file name case insensitive comparison of all references to find a match. It's version neutral because 
		/// only one version of a given assembly can be passed-in to the CodeDomProvider for compilation.
		/// </devdoc>
		private bool ContainsReference(System.Collections.Specialized.StringCollection references, string assemblyFile)
		{
			string simpleFileName = Path.GetFileName(assemblyFile);

			foreach (string file in references)
			{
				if (Path.GetFileName(file).Equals(simpleFileName, StringComparison.CurrentCultureIgnoreCase))
				{
					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Gets the <see cref="CodeCompileUnit"/>s generated so far.
		/// </summary>
		public CodeCompileUnit[] CompileUnits
		{
			get { return compileUnits.ToArray(); }
		}
	}
}
